#ifndef TDDASSERT_H
#define TDDASSERT_H

#ifndef _CPPUNWIND
#error TddAssert requires exceptions. If exceptions cannot be used in your test project, use the TDD_VERIFY* macros in tdd.h.
#endif

#include <atlstr.h>
#include "..\..\tdd.h"

namespace TDD
{
class Assert
{
	class AssertException : public TDD::TddException
	{
		CStringA m_message, m_file;
	public:
		AssertException(const CStringA& message, unsigned long line, __in_z const char* file)
			: TDD::TddException(line, "")
			, m_message(message)
			, m_file(file)
		{
			SetMessage(m_message.GetString());
			SetFile   (m_file.GetString());
		}
	};

	unsigned long m_line;
	const char* m_file;
public:
	Assert(unsigned long line, __in_z const char* file)
		: m_line(line)
		, m_file(file)
	{}
	virtual ~Assert() {}

	template <typename S, typename T> void AreEqual(const S& expected, const T& actual, const CString& message=CString())
	{
		CString csExpected = ToString(expected);
		CString csActual   = ToString(actual);
		if (csExpected != csActual)
		{
			CStringA cs(' ', 25 + csExpected.GetLength() + csActual.GetLength() + message.GetLength());
			cs  = "Expected <";
			cs += csExpected;
			cs += ">, Actual <";
			cs += csActual;
			cs += ">";
			if (!message.IsEmpty())
			{
				cs += " - ";
				cs += message;
			}

			throw AssertException(cs, m_line, m_file);
		}
	}
	template <typename S, typename T> void AreNotEqual(const S& expected, const T& actual, const CString& message=CString())
	{
		CString csActual   = ToString(actual);
		if (ToString(expected) == csActual)
		{
			CStringA cs(' ', 25 + csActual.GetLength() + message.GetLength());
			cs  = "Unexpected equality <";
			cs += csActual;
			cs += ">";
			if (!message.IsEmpty())
			{
				cs += " - ";
				cs += message;
			}

			throw AssertException(cs, m_line, m_file);
		}
	}
	template <typename T> void IsFalse(const T& actual, const CString& message=CString()) { return AreEqual(false, actual, message); }
	template <typename T> void IsTrue (const T& actual, const CString& message=CString()) { return AreEqual( true, actual, message); }
	void Fail(const CString& message) { throw AssertException(CStringA(message), m_line, m_file); }
};

	// helpers
	inline CString FromSignedInt(int i)
	{
		CString cs;
		cs.Format(_T("%d"), i);
		return cs;
	}
	inline CString FromUnsignedInt(unsigned int i)
	{
		CString cs;
		cs.Format(_T("%u"), i);
		return cs;
	}
	inline CString FromULONGLONG(ULONGLONG i)
	{
		CString cs;
		cs.Format(_T("%I64u"), i);
		return cs;
	}

	// some free functions
	inline CString ToString(const  char* t) { return CString(t); }
	inline CString ToString(const WCHAR* t) { return CString(t); }
	__pragma ( prefast(suppress: 25044, "complaining that BSTR should be const WCHAR*, but it's already const (appears to be PREfast bug)") )
	inline CString ToString(const BSTR   t) { return CString(t); }
	template <typename T> CString ToString(const T& t) { return t.MessageForTestWriter_YouMustWriteASpecializationOf_ToString_ForYourClass(); }
	// the line above will always generate a compiler error.
	// Implement free functions similar to these for your user-defined types
	template <> inline CString ToString<bool>			(const bool & t)			{ return FromSignedInt(t); }
	template <> inline CString ToString<int>			(const int & t)				{ return FromSignedInt(t); }
	template <> inline CString ToString<long>			(const long & t)			{ return FromSignedInt(t); }
	template <> inline CString ToString<short>			(const short & t)			{ return FromSignedInt(t); }
	template <> inline CString ToString<char>			(const char & t)			{ return FromSignedInt(t); }
	template <> inline CString ToString<signed char>	(const signed char & t)		{ return FromSignedInt(t); }
	template <> inline CString ToString<unsigned int>	(const unsigned int & t)	{ return FromUnsignedInt(t); }
	template <> inline CString ToString<unsigned long>	(const unsigned long & t)	{ return FromUnsignedInt(t); }
	template <> inline CString ToString<ULONGLONG>		(const ULONGLONG & t)		{ return FromULONGLONG(t); }
	template <> inline CString ToString<unsigned short>	(const unsigned short & t)	{ return FromUnsignedInt(t); }
	template <> inline CString ToString<unsigned char>	(const unsigned char & t)	{ return FromUnsignedInt(t); }

	template <> inline CString ToString<CStringA>(const CStringA & t) { return CString(t); }
	template <> inline CString ToString<CStringW>(const CStringW & t) { return CString(t); }
}

#define TddAssert(...) TDD::Assert(__LINE__, __FILE__)

#endif
